home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkGrab.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  46.5 KB  |  1,536 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkGrab.c --
  3.  *
  4.  *    This file provides procedures that implement grabs for Tk.
  5.  *
  6.  * Copyright (c) 1992-1994 The Regents of the University of California.
  7.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkGrab.c 1.52 97/03/21 11:14:34
  13.  */
  14.  
  15. #include "tkPort.h"
  16. #include "tkInt.h"
  17.  
  18. /*
  19.  * The grab state machine has four states: ungrabbed, button pressed,
  20.  * grabbed, and button pressed while grabbed.  In addition, there are
  21.  * three pieces of grab state information: the current grab window,
  22.  * the current restrict window, and whether the mouse is captured.
  23.  *
  24.  * The current grab window specifies the point in the Tk window
  25.  * heirarchy above which pointer events will not be reported.  Any
  26.  * window within the subtree below the grab window will continue to
  27.  * receive events as normal.  Events outside of the grab tree will be
  28.  * reported to the grab window.
  29.  *
  30.  * If the current restrict window is set, then all pointer events will
  31.  * be reported only to the restrict window.  The restrict window is
  32.  * normally set during an automatic button grab.
  33.  *
  34.  * The mouse capture state specifies whether the window system will
  35.  * report mouse events outside of any Tk toplevels.  This is set
  36.  * during a global grab or an automatic button grab.
  37.  *
  38.  * The transitions between different states is given in the following
  39.  * table:
  40.  * 
  41.  * Event\State    U    B    G    GB
  42.  * -----------    --    --    --    --
  43.  * FirstPress    B    B    GB    GB
  44.  * Press    B    B    G    GB
  45.  * Release    U    B    G    GB
  46.  * LastRelease    U    U    G    G
  47.  * Grab        G    G    G    G
  48.  * Ungrab    U    B    U    U
  49.  *
  50.  * Note: U=Ungrabbed, B=Button, G=Grabbed, GB=Grab and Button
  51.  *
  52.  * In addition, the following conditions are always true:
  53.  *
  54.  * State\Variable    Grab         Restrict         Capture
  55.  * --------------    ----         --------         -------
  56.  * Ungrabbed         0        0        0
  57.  * Button         0        1        1
  58.  * Grabbed         1        0        b/g
  59.  * Grab and Button     1        1        1
  60.  *
  61.  * Note: 0 means variable is set to NULL, 1 means variable is set to
  62.  * some window, b/g means the variable is set to a window if a button
  63.  * is currently down or a global grab is in effect.
  64.  *
  65.  * The final complication to all of this is enter and leave events.
  66.  * In order to correctly handle all of the various cases, Tk cannot
  67.  * rely on X enter/leave events in all situations.  The following
  68.  * describes the correct sequence of enter and leave events that
  69.  * should be observed by Tk scripts:
  70.  *
  71.  * Event(state)        Enter/Leave From -> To
  72.  * ------------        ----------------------
  73.  * LastRelease(B | GB): restrict window -> anc(grab window, event window)
  74.  * Grab(U | B):     event window -> anc(grab window, event window)
  75.  * Grab(G):        anc(old grab window, event window) ->
  76.  *                 anc(new grab window, event window)
  77.  * Grab(GB):        restrict window -> anc(new grab window, event window)
  78.  * Ungrab(G):        anc(grab window, event window) -> event window
  79.  * Ungrab(GB):        restrict window -> event window
  80.  *
  81.  * Note: anc(x,y) returns the least ancestor of y that is in the tree
  82.  * of x, terminating at toplevels.
  83.  */
  84.  
  85. /*
  86.  * The following structure is used to pass information to 
  87.  * GrabRestrictProc from EatGrabEvents.
  88.  */
  89.  
  90. typedef struct {
  91.     Display *display;        /* Display from which to discard events. */
  92.     unsigned int serial;    /* Serial number with which to compare. */
  93. } GrabInfo;
  94.  
  95. /*
  96.  * Bit definitions for grabFlags field of TkDisplay structures:
  97.  *
  98.  * GRAB_GLOBAL            1 means this is a global grab (we grabbed via
  99.  *                the server so all applications are locked out).
  100.  *                0 means this is a local grab that affects
  101.  *                only this application.
  102.  * GRAB_TEMP_GLOBAL        1 means we've temporarily grabbed via the
  103.  *                server because a button is down and we want
  104.  *                to make sure that we get the button-up
  105.  *                event.  The grab will be released when the
  106.  *                last mouse button goes up.
  107.  */
  108.  
  109. #define GRAB_GLOBAL        1
  110. #define GRAB_TEMP_GLOBAL    4
  111.  
  112. /*
  113.  * The following structure is a Tcl_Event that triggers a change in
  114.  * the grabWinPtr field of a display.  This event guarantees that
  115.  * the change occurs in the proper order relative to enter and leave
  116.  * events.
  117.  */
  118.  
  119. typedef struct NewGrabWinEvent {
  120.     Tcl_Event header;        /* Standard information for all Tcl events. */
  121.     TkDisplay *dispPtr;        /* Display whose grab window is to change. */
  122.     Window grabWindow;        /* New grab window for display.  This is
  123.                  * recorded instead of a (TkWindow *) because
  124.                  * it will allow us to detect cases where
  125.                  * the window is destroyed before this event
  126.                  * is processed. */
  127. } NewGrabWinEvent;
  128.  
  129. /*
  130.  * The following magic value is stored in the "send_event" field of
  131.  * EnterNotify and LeaveNotify events that are generated in this
  132.  * file.  This allows us to separate "real" events coming from the
  133.  * server from those that we generated.
  134.  */
  135.  
  136. #define GENERATED_EVENT_MAGIC ((Bool) 0x147321ac)
  137.  
  138. /*
  139.  * Mask that selects any of the state bits corresponding to buttons,
  140.  * plus masks that select individual buttons' bits:
  141.  */
  142.  
  143. #define ALL_BUTTONS \
  144.     (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask)
  145. static unsigned int buttonStates[] = {
  146.     Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask
  147. };
  148.  
  149. /*
  150.  * Forward declarations for procedures declared later in this file:
  151.  */
  152.  
  153. static void        EatGrabEvents _ANSI_ARGS_((TkDisplay *dispPtr,
  154.                 unsigned int serial));
  155. static TkWindow *    FindCommonAncestor _ANSI_ARGS_((TkWindow *winPtr1,
  156.                 TkWindow *winPtr2, int *countPtr1,
  157.                 int *countPtr2));
  158. static Tk_RestrictAction GrabRestrictProc _ANSI_ARGS_((ClientData arg,
  159.                 XEvent *eventPtr));
  160. static int        GrabWinEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
  161.                 int flags));
  162. static void        MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr,
  163.                 TkWindow *destPtr, int mode, int leaveEvents,
  164.                 int EnterEvents));
  165. static void        QueueGrabWindowChange _ANSI_ARGS_((TkDisplay *dispPtr,
  166.                 TkWindow *grabWinPtr));
  167. static void        ReleaseButtonGrab _ANSI_ARGS_((TkDisplay *dispPtr));
  168.  
  169. /*
  170.  *----------------------------------------------------------------------
  171.  *
  172.  * Tk_GrabCmd --
  173.  *
  174.  *    This procedure is invoked to process the "grab" Tcl command.
  175.  *    See the user documentation for details on what it does.
  176.  *
  177.  * Results:
  178.  *    A standard Tcl result.
  179.  *
  180.  * Side effects:
  181.  *    See the user documentation.
  182.  *
  183.  *----------------------------------------------------------------------
  184.  */
  185.  
  186.     /* ARGSUSED */
  187. int
  188. Tk_GrabCmd(clientData, interp, argc, argv)
  189.     ClientData clientData;    /* Main window associated with
  190.                  * interpreter. */
  191.     Tcl_Interp *interp;        /* Current interpreter. */
  192.     int argc;            /* Number of arguments. */
  193.     char **argv;        /* Argument strings. */
  194. {
  195.     int globalGrab, c;
  196.     Tk_Window tkwin;
  197.     TkDisplay *dispPtr;
  198.     size_t length;
  199.  
  200.     if (argc < 2) {
  201.     badArgs:
  202.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  203.         argv[0], " ?-global? window\" or \"", argv[0],
  204.         " option ?arg arg ...?\"", (char *) NULL);
  205.     return TCL_ERROR;
  206.     }
  207.     c = argv[1][0];
  208.     length = strlen(argv[1]);
  209.     if (c == '.') {
  210.     if (argc != 2) {
  211.         goto badArgs;
  212.     }
  213.     tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData);
  214.     if (tkwin == NULL) {
  215.         return TCL_ERROR;
  216.     }
  217.     return Tk_Grab(interp, tkwin, 0);
  218.     } else if ((c == '-') && (strncmp(argv[1], "-global", length) == 0)
  219.         && (length >= 2)) {
  220.     if (argc != 3) {
  221.         goto badArgs;
  222.     }
  223.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  224.     if (tkwin == NULL) {
  225.         return TCL_ERROR;
  226.     }
  227.     return Tk_Grab(interp, tkwin, 1);
  228.     } else if ((c == 'c') && (strncmp(argv[1], "current", length) == 0)) {
  229.     if (argc > 3) {
  230.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  231.             argv[0], " current ?window?\"", (char *) NULL);
  232.         return TCL_ERROR;
  233.     }
  234.     if (argc == 3) {
  235.         tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  236.         if (tkwin == NULL) {
  237.         return TCL_ERROR;
  238.         }
  239.         dispPtr = ((TkWindow *) tkwin)->dispPtr;
  240.         if (dispPtr->eventualGrabWinPtr != NULL) {
  241.         interp->result = dispPtr->eventualGrabWinPtr->pathName;
  242.         }
  243.     } else {
  244.         for (dispPtr = tkDisplayList; dispPtr != NULL;
  245.             dispPtr = dispPtr->nextPtr) {
  246.         if (dispPtr->eventualGrabWinPtr != NULL) {
  247.             Tcl_AppendElement(interp,
  248.                 dispPtr->eventualGrabWinPtr->pathName);
  249.         }
  250.         }
  251.     }
  252.     return TCL_OK;
  253.     } else if ((c == 'r') && (strncmp(argv[1], "release", length) == 0)) {
  254.     if (argc != 3) {
  255.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  256.             argv[0], " release window\"", (char *) NULL);
  257.         return TCL_ERROR;
  258.     }
  259.     tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  260.     if (tkwin == NULL) {
  261.         Tcl_ResetResult(interp);
  262.     } else {
  263.         Tk_Ungrab(tkwin);
  264.     }
  265.     } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0)
  266.         && (length >= 2)) {
  267.     if ((argc != 3) && (argc != 4)) {
  268.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  269.             argv[0], " set ?-global? window\"", (char *) NULL);
  270.         return TCL_ERROR;
  271.     }
  272.     if (argc == 3) {
  273.         globalGrab = 0;
  274.         tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData);
  275.     } else {
  276.         globalGrab = 1;
  277.         length = strlen(argv[2]);
  278.         if ((strncmp(argv[2], "-global", length) != 0) || (length < 2)) {
  279.         Tcl_AppendResult(interp, "bad argument \"", argv[2],
  280.             "\": must be \"", argv[0], " set ?-global? window\"",
  281.             (char *) NULL);
  282.         return TCL_ERROR;
  283.         }
  284.         tkwin = Tk_NameToWindow(interp, argv[3], (Tk_Window) clientData);
  285.     }
  286.     if (tkwin == NULL) {
  287.         return TCL_ERROR;
  288.     }
  289.     return Tk_Grab(interp, tkwin, globalGrab);
  290.     } else if ((c == 's') && (strncmp(argv[1], "status", length) == 0)
  291.         && (length >= 2)) {
  292.     TkWindow *winPtr;
  293.  
  294.     if (argc != 3) {
  295.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  296.             argv[0], " status window\"", (char *) NULL);
  297.         return TCL_ERROR;
  298.     }
  299.     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2],
  300.         (Tk_Window) clientData);
  301.     if (winPtr == NULL) {
  302.         return TCL_ERROR;
  303.     }
  304.     dispPtr = winPtr->dispPtr;
  305.     if (dispPtr->eventualGrabWinPtr != winPtr) {
  306.         interp->result = "none";
  307.     } else if (dispPtr->grabFlags & GRAB_GLOBAL) {
  308.         interp->result = "global";
  309.     } else {
  310.         interp->result = "local";
  311.     }
  312.     } else {
  313.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  314.         "\": must be current, release, set, or status",
  315.         (char *) NULL);
  316.     return TCL_ERROR;
  317.     }
  318.     return TCL_OK;
  319. }
  320.  
  321. /*
  322.  *----------------------------------------------------------------------
  323.  *
  324.  * Tk_Grab --
  325.  *
  326.  *    Grabs the pointer and keyboard, so that mouse-related events are
  327.  *    only reported relative to a given window and its descendants.
  328.  *
  329.  * Results:
  330.  *    A standard Tcl result is returned.  TCL_OK is the normal return
  331.  *    value;  if the grab could not be set then TCL_ERROR is returned
  332.  *    and interp->result will hold an error message.
  333.  *
  334.  * Side effects:
  335.  *    Once this call completes successfully, no window outside the
  336.  *    tree rooted at tkwin will receive pointer- or keyboard-related
  337.  *    events until the next call to Tk_Ungrab.  If a previous grab was
  338.  *    in effect within this application, then it is replaced with a new
  339.  *    one.
  340.  *
  341.  *----------------------------------------------------------------------
  342.  */
  343.  
  344. int
  345. Tk_Grab(interp, tkwin, grabGlobal)
  346.     Tcl_Interp *interp;            /* Used for error reporting. */
  347.     Tk_Window tkwin;            /* Window on whose behalf the pointer
  348.                      * is to be grabbed. */
  349.     int grabGlobal;            /* Non-zero means issue a grab to the
  350.                      * server so that no other application
  351.                      * gets mouse or keyboard events.
  352.                      * Zero means the grab only applies
  353.                      * within this application. */
  354. {
  355.     int grabResult, numTries;
  356.     TkWindow *winPtr = (TkWindow *) tkwin;
  357.     TkDisplay *dispPtr = winPtr->dispPtr;
  358.     TkWindow *winPtr2;
  359.     unsigned int serial;
  360.  
  361.     ReleaseButtonGrab(dispPtr);
  362.     if (dispPtr->eventualGrabWinPtr != NULL) {
  363.     if ((dispPtr->eventualGrabWinPtr == winPtr)
  364.         && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) {
  365.         return TCL_OK;
  366.     }
  367.     if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) {
  368.         alreadyGrabbed:
  369.         interp->result = "grab failed: another application has grab";
  370.         return TCL_ERROR;
  371.     }
  372.     Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr);
  373.     }
  374.  
  375.     Tk_MakeWindowExist(tkwin);
  376.     if (!grabGlobal) {
  377.     Window dummy1, dummy2;
  378.     int dummy3, dummy4, dummy5, dummy6;
  379.     unsigned int state;
  380.  
  381.     /*
  382.      * Local grab.  However, if any mouse buttons are down, turn
  383.      * it into a global grab temporarily, until the last button
  384.      * goes up.  This does two things: (a) it makes sure that we
  385.      * see the button-up event;  and (b) it allows us to track mouse
  386.      * motion among all of the windows of this application.
  387.      */
  388.  
  389.     dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL);
  390.     XQueryPointer(dispPtr->display, winPtr->window, &dummy1,
  391.         &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &state);
  392.     if ((state & ALL_BUTTONS) != 0) {
  393.         dispPtr->grabFlags |= GRAB_TEMP_GLOBAL;
  394.         goto setGlobalGrab;
  395.     }
  396.     } else {
  397.     dispPtr->grabFlags |= GRAB_GLOBAL;
  398.     setGlobalGrab:
  399.  
  400.     /*
  401.      * Tricky point: must ungrab before grabbing.  This is needed
  402.      * in case there is a button auto-grab already in effect.  If
  403.      * there is, and the mouse has moved to a different window, X
  404.      * won't generate enter and leave events to move the mouse if
  405.      * we grab without ungrabbing.
  406.      */
  407.  
  408.     XUngrabPointer(dispPtr->display, CurrentTime);
  409.     serial = NextRequest(dispPtr->display);
  410.  
  411.     /*
  412.      * Another tricky point: there are races with some window
  413.      * managers that can cause grabs to fail because the window
  414.      * manager hasn't released its grab quickly enough.  To work
  415.      * around this problem, retry a few times after AlreadyGrabbed
  416.      * errors to give the grab release enough time to register with
  417.      * the server.
  418.      */
  419.  
  420.     grabResult = 0;            /* Needed only to prevent gcc
  421.                      * compiler warnings. */
  422.     for (numTries = 0; numTries < 10; numTries++) {
  423.         grabResult = XGrabPointer(dispPtr->display, winPtr->window,
  424.             True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask
  425.             |PointerMotionMask, GrabModeAsync, GrabModeAsync, None,
  426.             None, CurrentTime);
  427.         if (grabResult != AlreadyGrabbed) {
  428.         break;
  429.         }
  430.         Tcl_Sleep(100);
  431.     }
  432.     if (grabResult != 0) {
  433.         grabError:
  434.         if (grabResult == GrabNotViewable) {
  435.         interp->result = "grab failed: window not viewable";
  436.         } else if (grabResult == AlreadyGrabbed) {
  437.         goto alreadyGrabbed;
  438.         } else if (grabResult == GrabFrozen) {
  439.         interp->result = "grab failed: keyboard or pointer frozen";
  440.         } else if (grabResult == GrabInvalidTime) {
  441.         interp->result = "grab failed: invalid time";
  442.         } else {
  443.         char msg[100];
  444.     
  445.         sprintf(msg, "grab failed for unknown reason (code %d)",
  446.             grabResult);
  447.         Tcl_AppendResult(interp, msg, (char *) NULL);
  448.         }
  449.         return TCL_ERROR;
  450.     }
  451.     grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin),
  452.         False, GrabModeAsync, GrabModeAsync, CurrentTime);
  453.     if (grabResult != 0) {
  454.         XUngrabPointer(dispPtr->display, CurrentTime);
  455.         goto grabError;
  456.     }
  457.  
  458.     /*
  459.      * Eat up any grab-related events generated by the server for the
  460.      * grab.  There are several reasons for doing this:
  461.      *
  462.      * 1. We have to synthesize the events for local grabs anyway, since
  463.      *    the server doesn't participate in them.
  464.      * 2. The server doesn't always generate the right events for global
  465.      *    grabs (e.g. it generates events even if the current window is
  466.      *    in the grab tree, which we don't want).
  467.      * 3. We want all the grab-related events to be processed immediately
  468.      *    (before other events that are already queued); events coming
  469.      *    from the server will be in the wrong place, but events we
  470.      *    synthesize here will go to the front of the queue.
  471.      */
  472.  
  473.     EatGrabEvents(dispPtr, serial);
  474.     }
  475.  
  476.     /*
  477.      * Synthesize leave events to move the pointer from its current window
  478.      * up to the lowest ancestor that it has in common with the grab window.
  479.      * However, only do this if the pointer is outside the grab window's
  480.      * subtree but inside the grab window's application.
  481.      */
  482.  
  483.     if ((dispPtr->serverWinPtr != NULL)
  484.         && (dispPtr->serverWinPtr->mainPtr == winPtr->mainPtr)) {
  485.     for (winPtr2 = dispPtr->serverWinPtr; ; winPtr2 = winPtr2->parentPtr) {
  486.         if (winPtr2 == winPtr) {
  487.         break;
  488.         }
  489.         if (winPtr2 == NULL) {
  490.         MovePointer2(dispPtr->serverWinPtr, winPtr, NotifyGrab, 1, 0);
  491.         break;
  492.         }
  493.     }
  494.     }
  495.     QueueGrabWindowChange(dispPtr, winPtr);
  496.     return TCL_OK;
  497. }
  498.  
  499. /*
  500.  *----------------------------------------------------------------------
  501.  *
  502.  * Tk_Ungrab --
  503.  *
  504.  *    Releases a grab on the mouse pointer and keyboard, if there
  505.  *    is one set on the specified window.
  506.  *
  507.  * Results:
  508.  *    None.
  509.  *
  510.  * Side effects:
  511.  *    Pointer and keyboard events will start being delivered to other
  512.  *    windows again.
  513.  *
  514.  *----------------------------------------------------------------------
  515.  */
  516.  
  517. void
  518. Tk_Ungrab(tkwin)
  519.     Tk_Window tkwin;            /* Window whose grab should be
  520.                      * released. */
  521. {
  522.     TkDisplay *dispPtr;
  523.     TkWindow *grabWinPtr, *winPtr;
  524.     unsigned int serial;
  525.  
  526.     grabWinPtr = (TkWindow *) tkwin;
  527.     dispPtr = grabWinPtr->dispPtr;
  528.     if (grabWinPtr != dispPtr->eventualGrabWinPtr) {
  529.     return;
  530.     }
  531.     ReleaseButtonGrab(dispPtr);
  532.     QueueGrabWindowChange(dispPtr, (TkWindow *) NULL);
  533.     if (dispPtr->grabFlags & (GRAB_GLOBAL|GRAB_TEMP_GLOBAL)) {
  534.     dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL);
  535.     serial = NextRequest(dispPtr->display);
  536.     XUngrabPointer(dispPtr->display, CurrentTime);
  537.     XUngrabKeyboard(dispPtr->display, CurrentTime);
  538.     EatGrabEvents(dispPtr, serial);
  539.     }
  540.  
  541.     /*
  542.      * Generate events to move the pointer back to the window where it
  543.      * really is.  Some notes:
  544.      * 1. As with grabs, only do this if the "real" window is not a
  545.      *    descendant of the grab window, since in this case the pointer
  546.      *    is already where it's supposed to be.
  547.      * 2. If the "real" window is in some other application then don't
  548.      *    generate any events at all, since everything's already been
  549.      *    reported correctly.
  550.      * 3. Only generate enter events.  Don't generate leave events,
  551.      *    because we never told the lower-level windows that they
  552.      *    had the pointer in the first place.
  553.      */
  554.  
  555.     for (winPtr = dispPtr->serverWinPtr; ; winPtr = winPtr->parentPtr) {
  556.     if (winPtr == grabWinPtr) {
  557.         break;
  558.     }
  559.     if (winPtr == NULL) {
  560.         if ((dispPtr->serverWinPtr == NULL) ||
  561.             (dispPtr->serverWinPtr->mainPtr == grabWinPtr->mainPtr)) {
  562.         MovePointer2(grabWinPtr, dispPtr->serverWinPtr,
  563.             NotifyUngrab, 0, 1);
  564.         }
  565.         break;
  566.     }
  567.     }
  568. }
  569.  
  570. /*
  571.  *----------------------------------------------------------------------
  572.  *
  573.  * ReleaseButtonGrab --
  574.  *
  575.  *    This procedure is called to release a simulated button grab, if
  576.  *    there is one in effect.  A button grab is present whenever
  577.  *    dispPtr->buttonWinPtr is non-NULL or when the GRAB_TEMP_GLOBAL
  578.  *    flag is set.
  579.  *
  580.  * Results:
  581.  *    None.
  582.  *
  583.  * Side effects:
  584.  *    DispPtr->buttonWinPtr is reset to NULL, and enter and leave
  585.  *    events are generated if necessary to move the pointer from
  586.  *    the button grab window to its current window.
  587.  *
  588.  *----------------------------------------------------------------------
  589.  */
  590.  
  591. static void
  592. ReleaseButtonGrab(dispPtr)
  593.     register TkDisplay *dispPtr;    /* Display whose button grab is to be
  594.                      * released. */
  595. {
  596.     unsigned int serial;
  597.  
  598.     if (dispPtr->buttonWinPtr != NULL) {
  599.     if (dispPtr->buttonWinPtr != dispPtr->serverWinPtr) {
  600.         MovePointer2(dispPtr->buttonWinPtr, dispPtr->serverWinPtr,
  601.             NotifyUngrab, 1, 1);
  602.     }
  603.     dispPtr->buttonWinPtr = NULL;
  604.     }
  605.     if (dispPtr->grabFlags & GRAB_TEMP_GLOBAL) {
  606.     dispPtr->grabFlags &= ~GRAB_TEMP_GLOBAL;
  607.     serial = NextRequest(dispPtr->display);
  608.     XUngrabPointer(dispPtr->display, CurrentTime);
  609.     XUngrabKeyboard(dispPtr->display, CurrentTime);
  610.     EatGrabEvents(dispPtr, serial);
  611.     }
  612. }
  613.  
  614. /*
  615.  *----------------------------------------------------------------------
  616.  *
  617.  * TkPointerEvent --
  618.  *
  619.  *    This procedure is called for each pointer-related event, before
  620.  *    the event has been processed.  It does various things to make
  621.  *    grabs work correctly.
  622.  *
  623.  * Results:
  624.  *    If the return value is 1 it means the event should be processed
  625.  *    (event handlers should be invoked).  If the return value is 0
  626.  *    it means the event should be ignored in order to make grabs
  627.  *    work correctly.  In some cases this procedure modifies the event.
  628.  *
  629.  * Side effects:
  630.  *    Grab state information may be updated.  New events may also be
  631.  *    pushed back onto the event queue to replace or augment the
  632.  *    one passed in here.
  633.  *
  634.  *----------------------------------------------------------------------
  635.  */
  636.  
  637. int
  638. TkPointerEvent(eventPtr, winPtr)
  639.     register XEvent *eventPtr;        /* Pointer to the event. */
  640.     TkWindow *winPtr;            /* Tk's information for window
  641.                      * where event was reported. */
  642. {
  643.     register TkWindow *winPtr2;
  644.     TkDisplay *dispPtr = winPtr->dispPtr;
  645.     unsigned int serial;
  646.     int outsideGrabTree = 0;
  647.     int ancestorOfGrab = 0;
  648.     int appGrabbed = 0;            /* Non-zero means event is being
  649.                      * reported to an application that is
  650.                      * affected by the grab. */
  651.  
  652.     /*
  653.      * Collect information about the grab (if any).
  654.      */
  655.  
  656.     switch (TkGrabState(winPtr)) {
  657.     case TK_GRAB_IN_TREE:
  658.         appGrabbed = 1;
  659.         break;
  660.     case TK_GRAB_ANCESTOR:
  661.         appGrabbed = 1;
  662.         outsideGrabTree = 1;
  663.         ancestorOfGrab = 1;
  664.         break;
  665.     case TK_GRAB_EXCLUDED:
  666.         appGrabbed = 1;
  667.         outsideGrabTree = 1;
  668.         break;
  669.     }
  670.  
  671.     if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) {
  672.     /*
  673.      * Keep track of what window the mouse is *really* over.
  674.      * Any events that we generate have a special send_event value,
  675.      * which is detected below and used to ignore the event for
  676.      * purposes of setting serverWinPtr.
  677.      */
  678.  
  679.     if (eventPtr->xcrossing.send_event != GENERATED_EVENT_MAGIC) {
  680.         if ((eventPtr->type == LeaveNotify) &&
  681.             (winPtr->flags & TK_TOP_LEVEL)) {
  682.         dispPtr->serverWinPtr = NULL;
  683.         } else {
  684.         dispPtr->serverWinPtr = winPtr;
  685.         }
  686.     }
  687.  
  688.     /*
  689.      * When a grab is active, X continues to report enter and leave
  690.      * events for windows outside the tree of the grab window:
  691.      * 1. Detect these events and ignore them except for
  692.      *    windows above the grab window.
  693.      * 2. Allow Enter and Leave events to pass through the
  694.      *    windows above the grab window, but never let them
  695.      *    end up with the pointer *in* one of those windows.
  696.      */
  697.  
  698.     if (dispPtr->grabWinPtr != NULL) {
  699.         if (outsideGrabTree && appGrabbed) {
  700.         if (!ancestorOfGrab) {
  701.             return 0;
  702.         }
  703.         switch (eventPtr->xcrossing.detail) {
  704.             case NotifyInferior:
  705.             return 0;
  706.             case NotifyAncestor:
  707.             eventPtr->xcrossing.detail = NotifyVirtual;
  708.             break;
  709.             case NotifyNonlinear:
  710.             eventPtr->xcrossing.detail = NotifyNonlinearVirtual;
  711.             break;
  712.         }
  713.         }
  714.  
  715.         /*
  716.          * Make buttons have the same grab-like behavior inside a grab
  717.          * as they do outside a grab:  do this by ignoring enter and
  718.          * leave events except for the window in which the button was
  719.          * pressed.
  720.          */
  721.  
  722.         if ((dispPtr->buttonWinPtr != NULL)
  723.             && (winPtr != dispPtr->buttonWinPtr)) {
  724.         return 0;
  725.         }
  726.     }
  727.     return 1;
  728.     }
  729.  
  730.     if (!appGrabbed) {
  731.     return 1;
  732.     }
  733.  
  734.     if (eventPtr->type == MotionNotify) {
  735.     /*
  736.      * When grabs are active, X reports motion events relative to the
  737.      * window under the pointer.  Instead, it should report the events
  738.      * relative to the window the button went down in, if there is a
  739.      * button down.  Otherwise, if the pointer window is outside the
  740.      * subtree of the grab window, the events should be reported
  741.      * relative to the grab window.  Otherwise, the event should be
  742.      * reported to the pointer window.
  743.      */
  744.  
  745.     winPtr2 = winPtr;
  746.     if (dispPtr->buttonWinPtr != NULL) {
  747.         winPtr2 = dispPtr->buttonWinPtr;
  748.     } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) {
  749.         winPtr2 = dispPtr->grabWinPtr;
  750.     }
  751.     if (winPtr2 != winPtr) {
  752.         TkChangeEventWindow(eventPtr, winPtr2);
  753.         Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
  754.         return 0;
  755.     }
  756.     return 1;
  757.     }
  758.  
  759.     /*
  760.      * Process ButtonPress and ButtonRelease events:
  761.      * 1. Keep track of whether a button is down and what window it
  762.      *    went down in.
  763.      * 2. If the first button goes down outside the grab tree, pretend
  764.      *    it went down in the grab window.  Note: it's important to
  765.      *    redirect events to the grab window like this in order to make
  766.      *    things like menus work, where button presses outside the
  767.      *    grabbed menu need to be seen.  An application can always
  768.      *    ignore the events if they occur outside its window.
  769.      * 3. If a button press or release occurs outside the window where
  770.      *    the first button was pressed, retarget the event so it's reported
  771.      *    to the window where the first button was pressed.
  772.      * 4. If the last button is released in a window different than where
  773.      *    the first button was pressed, generate Enter/Leave events to
  774.      *    move the mouse from the button window to its current window.
  775.      * 5. If the grab is set at a time when a button is already down, or
  776.      *    if the window where the button was pressed was deleted, then
  777.      *    dispPtr->buttonWinPtr will stay NULL.  Just forget about the
  778.      *    auto-grab for the button press;  events will go to whatever
  779.      *    window contains the pointer.  If this window isn't in the grab
  780.      *    tree then redirect events to the grab window.
  781.      * 6. When a button is pressed during a local grab, the X server sets
  782.      *    a grab of its own, since it doesn't even know about our local
  783.      *    grab.  This causes enter and leave events no longer to be
  784.      *    generated in the same way as for global grabs.  To eliminate this
  785.      *    problem, set a temporary global grab when the first button goes
  786.      *    down and release it when the last button comes up.
  787.      */
  788.  
  789.     if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
  790.     winPtr2 = dispPtr->buttonWinPtr;
  791.     if (winPtr2 == NULL) {
  792.         if (outsideGrabTree) {
  793.         winPtr2 = dispPtr->grabWinPtr;            /* Note 5. */
  794.         } else {
  795.         winPtr2 = winPtr;                /* Note 5. */
  796.         }
  797.     }
  798.     if (eventPtr->type == ButtonPress) {
  799.         if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) {
  800.         if (outsideGrabTree) {
  801.             TkChangeEventWindow(eventPtr, dispPtr->grabWinPtr);
  802.             Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
  803.             return 0;                    /* Note 2. */
  804.         }
  805.         if (!(dispPtr->grabFlags & GRAB_GLOBAL)) {    /* Note 6. */
  806.             serial = NextRequest(dispPtr->display);
  807.             if (XGrabPointer(dispPtr->display,
  808.                 dispPtr->grabWinPtr->window, True,
  809.                 ButtonPressMask|ButtonReleaseMask|ButtonMotionMask,
  810.                 GrabModeAsync, GrabModeAsync, None, None,
  811.                 CurrentTime) == 0) {
  812.             EatGrabEvents(dispPtr, serial);
  813.             if (XGrabKeyboard(dispPtr->display, winPtr->window,
  814.                 False, GrabModeAsync, GrabModeAsync,
  815.                 CurrentTime) == 0) {
  816.                 dispPtr->grabFlags |= GRAB_TEMP_GLOBAL;
  817.             } else {
  818.                 XUngrabPointer(dispPtr->display, CurrentTime);
  819.             }
  820.             }
  821.         }
  822.         dispPtr->buttonWinPtr = winPtr;
  823.         return 1;
  824.         }
  825.     } else {
  826.         if ((eventPtr->xbutton.state & ALL_BUTTONS)
  827.             == buttonStates[eventPtr->xbutton.button - Button1]) {
  828.         ReleaseButtonGrab(dispPtr);            /* Note 4. */
  829.         }
  830.     }
  831.     if (winPtr2 != winPtr) {
  832.         TkChangeEventWindow(eventPtr, winPtr2);
  833.         Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD);
  834.         return 0;                        /* Note 3. */
  835.     }
  836.     }
  837.  
  838.     return 1;
  839. }
  840.  
  841. /*
  842.  *----------------------------------------------------------------------
  843.  *
  844.  * TkChangeEventWindow --
  845.  *
  846.  *    Given an event and a new window to which the event should be
  847.  *    retargeted, modify fields of the event so that the event is
  848.  *    properly retargeted to the new window.
  849.  *
  850.  * Results:
  851.  *    The following fields of eventPtr are modified:  window,
  852.  *    subwindow, x, y, same_screen.
  853.  *
  854.  * Side effects:
  855.  *    None.
  856.  *
  857.  *----------------------------------------------------------------------
  858.  */
  859.  
  860. void
  861. TkChangeEventWindow(eventPtr, winPtr)
  862.     register XEvent *eventPtr;    /* Event to retarget.  Must have
  863.                  * type ButtonPress, ButtonRelease, KeyPress,
  864.                  * KeyRelease, MotionNotify, EnterNotify,
  865.                  * or LeaveNotify. */
  866.     TkWindow *winPtr;        /* New target window for event. */
  867. {
  868.     int x, y, sameScreen, bd;
  869.     register TkWindow *childPtr;
  870.  
  871.     eventPtr->xmotion.window = Tk_WindowId(winPtr);
  872.     if (eventPtr->xmotion.root ==
  873.         RootWindow(winPtr->display, winPtr->screenNum)) {
  874.     Tk_GetRootCoords((Tk_Window) winPtr, &x, &y);
  875.     eventPtr->xmotion.x = eventPtr->xmotion.x_root - x;
  876.     eventPtr->xmotion.y = eventPtr->xmotion.y_root - y;
  877.     eventPtr->xmotion.subwindow = None;
  878.     for (childPtr = winPtr->childList; childPtr != NULL;
  879.         childPtr = childPtr->nextPtr) {
  880.         if (childPtr->flags & TK_TOP_LEVEL) {
  881.         continue;
  882.         }
  883.         x = eventPtr->xmotion.x - childPtr->changes.x;
  884.         y = eventPtr->xmotion.y - childPtr->changes.y;
  885.         bd = childPtr->changes.border_width;
  886.         if ((x >= -bd) && (y >= -bd)
  887.             && (x < (childPtr->changes.width + bd))
  888.             && (y < (childPtr->changes.height + bd))) {
  889.         eventPtr->xmotion.subwindow = childPtr->window;
  890.         }
  891.     }
  892.     sameScreen = 1;
  893.     } else {
  894.     eventPtr->xmotion.x = 0;
  895.     eventPtr->xmotion.y = 0;
  896.     eventPtr->xmotion.subwindow = None;
  897.     sameScreen = 0;
  898.     }
  899.     if (eventPtr->type == MotionNotify) {
  900.     eventPtr->xmotion.same_screen = sameScreen;
  901.     } else {
  902.     eventPtr->xbutton.same_screen = sameScreen;
  903.     }
  904. }
  905.  
  906. /*
  907.  *----------------------------------------------------------------------
  908.  *
  909.  * TkInOutEvents --
  910.  *
  911.  *    This procedure synthesizes EnterNotify and LeaveNotify events
  912.  *    to correctly transfer the pointer from one window to another.
  913.  *    It can also be used to generate FocusIn and FocusOut events
  914.  *    to move the input focus.
  915.  *
  916.  * Results:
  917.  *    None.
  918.  *
  919.  * Side effects:
  920.  *    Synthesized events may be pushed back onto the event queue.
  921.  *    The event pointed to by eventPtr is modified.
  922.  *
  923.  *----------------------------------------------------------------------
  924.  */
  925.  
  926. void
  927. TkInOutEvents(eventPtr, sourcePtr, destPtr, leaveType, enterType, position)
  928.     XEvent *eventPtr;        /* A template X event.  Must have all fields
  929.                  * properly set except for type, window,
  930.                  * subwindow, x, y, detail, and same_screen
  931.                  * (Not all of these fields are valid for
  932.                  * FocusIn/FocusOut events;  x_root and y_root
  933.                  * must be valid for Enter/Leave events, even
  934.                  * though x and y needn't be valid). */
  935.     TkWindow *sourcePtr;    /* Window that used to have the pointer or
  936.                  * focus (NULL means it was not in a window
  937.                  * managed by this process). */
  938.     TkWindow *destPtr;        /* Window that is to end up with the pointer
  939.                  * or focus (NULL means it's not one managed
  940.                  * by this process). */
  941.     int leaveType;        /* Type of events to generate for windows
  942.                  * being left (LeaveNotify or FocusOut).  0
  943.                  * means don't generate leave events. */
  944.     int enterType;        /* Type of events to generate for windows
  945.                  * being entered (EnterNotify or FocusIn).  0
  946.                  * means don't generate enter events. */
  947.     Tcl_QueuePosition position;    /* Position at which events are added to
  948.                  * the system event queue. */
  949. {
  950.     register TkWindow *winPtr;
  951.     int upLevels, downLevels, i, j, focus;
  952.  
  953.     /*
  954.      * There are four possible cases to deal with:
  955.      *
  956.      * 1. SourcePtr and destPtr are the same.  There's nothing to do in
  957.      *    this case.
  958.      * 2. SourcePtr is an ancestor of destPtr in the same top-level
  959.      *    window.  Must generate events down the window tree from source
  960.      *    to dest.
  961.      * 3. DestPtr is an ancestor of sourcePtr in the same top-level
  962.      *    window.  Must generate events up the window tree from sourcePtr
  963.      *    to destPtr.
  964.      * 4. All other cases.  Must first generate events up the window tree
  965.      *    from sourcePtr to its top-level, then down from destPtr's
  966.      *    top-level to destPtr. This form is called "non-linear."
  967.      *
  968.      * The call to FindCommonAncestor separates these four cases and decides
  969.      * how many levels up and down events have to be generated for.
  970.      */
  971.  
  972.     if (sourcePtr == destPtr) {
  973.     return;
  974.     }
  975.     if ((leaveType == FocusOut) || (enterType == FocusIn)) {
  976.     focus = 1;
  977.     } else {
  978.     focus = 0;
  979.     }
  980.     FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels);
  981.  
  982.     /*
  983.      * Generate enter/leave events and add them to the grab event queue.
  984.      */
  985.  
  986.  
  987. #define QUEUE(w, t, d)                    \
  988.     if (w->window != None) {                \
  989.     eventPtr->type = t;                \
  990.     if (focus) {                    \
  991.         eventPtr->xfocus.window = w->window;    \
  992.         eventPtr->xfocus.detail = d;        \
  993.     } else {                    \
  994.         eventPtr->xcrossing.detail = d;        \
  995.         TkChangeEventWindow(eventPtr, w);        \
  996.     }                        \
  997.     Tk_QueueWindowEvent(eventPtr, position);    \
  998.     }
  999.  
  1000.     if (downLevels == 0) {
  1001.     
  1002.     /*
  1003.      * SourcePtr is an inferior of destPtr.
  1004.      */
  1005.  
  1006.     if (leaveType != 0) {
  1007.         QUEUE(sourcePtr, leaveType, NotifyAncestor);
  1008.         for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0;
  1009.             winPtr = winPtr->parentPtr, i--) {
  1010.         QUEUE(winPtr, leaveType, NotifyVirtual);
  1011.         }
  1012.     }
  1013.     if ((enterType != 0) && (destPtr != NULL)) {
  1014.         QUEUE(destPtr, enterType, NotifyInferior);
  1015.     }
  1016.     } else if (upLevels == 0) {
  1017.  
  1018.     /*
  1019.      * DestPtr is an inferior of sourcePtr.
  1020.      */
  1021.  
  1022.     if ((leaveType != 0) && (sourcePtr != NULL)) {
  1023.         QUEUE(sourcePtr, leaveType, NotifyInferior);
  1024.     }
  1025.     if (enterType != 0) {
  1026.         for (i = downLevels-1; i > 0; i--) {
  1027.         for (winPtr = destPtr->parentPtr, j = 1; j < i;
  1028.             winPtr = winPtr->parentPtr, j++) {
  1029.         }
  1030.         QUEUE(winPtr, enterType, NotifyVirtual);
  1031.         }
  1032.         if (destPtr != NULL) {
  1033.         QUEUE(destPtr, enterType, NotifyAncestor);
  1034.         }
  1035.     }
  1036.     } else {
  1037.  
  1038.     /*
  1039.      * Non-linear:  neither window is an inferior of the other.
  1040.      */
  1041.  
  1042.     if (leaveType != 0) {
  1043.         QUEUE(sourcePtr, leaveType, NotifyNonlinear);
  1044.         for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0;
  1045.             winPtr = winPtr->parentPtr, i--) {
  1046.         QUEUE(winPtr, leaveType, NotifyNonlinearVirtual);
  1047.         }
  1048.     }
  1049.     if (enterType != 0) {
  1050.         for (i = downLevels-1; i > 0; i--) {
  1051.         for (winPtr = destPtr->parentPtr, j = 1; j < i;
  1052.             winPtr = winPtr->parentPtr, j++) {
  1053.         }
  1054.         QUEUE(winPtr, enterType, NotifyNonlinearVirtual);
  1055.         }
  1056.         if (destPtr != NULL) {
  1057.         QUEUE(destPtr, enterType, NotifyNonlinear);
  1058.         }
  1059.     }
  1060.     }
  1061. }
  1062.  
  1063. /*
  1064.  *----------------------------------------------------------------------
  1065.  *
  1066.  * MovePointer2 --
  1067.  *
  1068.  *    This procedure synthesizes  EnterNotify and LeaveNotify events
  1069.  *    to correctly transfer the pointer from one window to another.
  1070.  *    It is different from TkInOutEvents in that no template X event
  1071.  *    needs to be supplied;  this procedure generates the template
  1072.  *    event and calls TkInOutEvents.
  1073.  *
  1074.  * Results:
  1075.  *    None.
  1076.  *
  1077.  * Side effects:
  1078.  *    Synthesized events may be pushed back onto the event queue.
  1079.  *
  1080.  *----------------------------------------------------------------------
  1081.  */
  1082.  
  1083. static void
  1084. MovePointer2(sourcePtr, destPtr, mode, leaveEvents, enterEvents)
  1085.     TkWindow *sourcePtr;    /* Window currently containing pointer (NULL
  1086.                  * means it's not one managed by this
  1087.                  * process). */
  1088.     TkWindow *destPtr;        /* Window that is to end up containing the
  1089.                  * pointer (NULL means it's not one managed
  1090.                  * by this process). */
  1091.     int mode;            /* Mode for enter/leave events, such as
  1092.                  * NotifyNormal or NotifyUngrab. */
  1093.     int leaveEvents;        /* Non-zero means generate leave events for the
  1094.                  * windows being left.  Zero means don't
  1095.                  * generate leave events. */
  1096.     int enterEvents;        /* Non-zero means generate enter events for the
  1097.                  * windows being entered.  Zero means don't
  1098.                  * generate enter events. */
  1099. {
  1100.     XEvent event;
  1101.     Window dummy1, dummy2;
  1102.     int dummy3, dummy4;
  1103.     TkWindow *winPtr;
  1104.  
  1105.     winPtr = sourcePtr;
  1106.     if ((winPtr == NULL) || (winPtr->window == None)) {
  1107.     winPtr = destPtr;
  1108.     if ((winPtr == NULL) || (winPtr->window == None)) {
  1109.         return;
  1110.     }
  1111.     }
  1112.  
  1113.     event.xcrossing.serial = LastKnownRequestProcessed(
  1114.     winPtr->display);
  1115.     event.xcrossing.send_event = GENERATED_EVENT_MAGIC;
  1116.     event.xcrossing.display = winPtr->display;
  1117.     event.xcrossing.root = RootWindow(winPtr->display,
  1118.         winPtr->screenNum);
  1119.     event.xcrossing.time = TkCurrentTime(winPtr->dispPtr);
  1120.     XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2,
  1121.         &event.xcrossing.x_root, &event.xcrossing.y_root,
  1122.         &dummy3, &dummy4, &event.xcrossing.state);
  1123.     event.xcrossing.mode = mode;
  1124.     event.xcrossing.focus = False;
  1125.     TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0,
  1126.         (enterEvents) ? EnterNotify : 0, TCL_QUEUE_MARK);
  1127. }
  1128.  
  1129. /*
  1130.  *----------------------------------------------------------------------
  1131.  *
  1132.  * TkGrabDeadWindow --
  1133.  *
  1134.  *    This procedure is invoked whenever a window is deleted, so that
  1135.  *    grab-related cleanup can be performed.
  1136.  *
  1137.  * Results:
  1138.  *    None.
  1139.  *
  1140.  * Side effects:
  1141.  *    Various cleanups happen, such as generating events to move the
  1142.  *    pointer back to its "natural" window as if an ungrab had been
  1143.  *    done.  See the code.
  1144.  *
  1145.  *----------------------------------------------------------------------
  1146.  */
  1147.  
  1148. void
  1149. TkGrabDeadWindow(winPtr)
  1150.     register TkWindow *winPtr;        /* Window that is in the process
  1151.                      * of being deleted. */
  1152. {
  1153.     TkDisplay *dispPtr = winPtr->dispPtr;
  1154.  
  1155.     if (dispPtr->eventualGrabWinPtr == winPtr) {
  1156.     /*
  1157.      * Grab window was deleted.  Release the grab.
  1158.      */
  1159.  
  1160.     Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr);
  1161.     } else if (dispPtr->buttonWinPtr == winPtr) {
  1162.     ReleaseButtonGrab(dispPtr);
  1163.     }
  1164.     if (dispPtr->serverWinPtr == winPtr) {
  1165.     if (winPtr->flags & TK_TOP_LEVEL) {
  1166.         dispPtr->serverWinPtr = NULL;
  1167.     } else {
  1168.         dispPtr->serverWinPtr = winPtr->parentPtr;
  1169.     }
  1170.     }
  1171.     if (dispPtr->grabWinPtr == winPtr) {
  1172.     dispPtr->grabWinPtr = NULL;
  1173.     }
  1174. }
  1175.  
  1176. /*
  1177.  *----------------------------------------------------------------------
  1178.  *
  1179.  * EatGrabEvents --
  1180.  *
  1181.  *    This procedure is called to eliminate any Enter, Leave,
  1182.  *    FocusIn, or FocusOut events in the event queue for a
  1183.  *    display that have mode NotifyGrab or NotifyUngrab and
  1184.  *    have a serial number no less than a given value and are not
  1185.  *    generated by the grab module.
  1186.  *
  1187.  * Results:
  1188.  *    None.
  1189.  *
  1190.  * Side effects:
  1191.  *    DispPtr's display gets sync-ed, and some of the events get
  1192.  *    removed from the Tk event queue.
  1193.  *
  1194.  *----------------------------------------------------------------------
  1195.  */
  1196.  
  1197. static void
  1198. EatGrabEvents(dispPtr, serial)
  1199.     TkDisplay *dispPtr;        /* Display from which to consume events. */
  1200.     unsigned int serial;    /* Only discard events that have a serial
  1201.                  * number at least this great. */
  1202. {
  1203.     Tk_RestrictProc *oldProc;
  1204.     GrabInfo info;
  1205.     ClientData oldArg, dummy;
  1206.  
  1207.     info.display = dispPtr->display;
  1208.     info.serial = serial;
  1209.     TkpSync(info.display);
  1210.     oldProc = Tk_RestrictEvents(GrabRestrictProc, (ClientData)&info, &oldArg);
  1211.     while (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
  1212.     }
  1213.     Tk_RestrictEvents(oldProc, oldArg, &dummy);
  1214. }
  1215.  
  1216. /*
  1217.  *----------------------------------------------------------------------
  1218.  *
  1219.  * GrabRestrictProc --
  1220.  *
  1221.  *    A Tk_RestrictProc used by EatGrabEvents to eliminate any
  1222.  *    Enter, Leave, FocusIn, or FocusOut events in the event queue
  1223.  *    for a display that has mode NotifyGrab or NotifyUngrab and
  1224.  *    have a serial number no less than a given value.
  1225.  *
  1226.  * Results:
  1227.  *    Returns either TK_DISCARD_EVENT or TK_DEFER_EVENT.
  1228.  *
  1229.  * Side effects:
  1230.  *    None.
  1231.  *
  1232.  *----------------------------------------------------------------------
  1233.  */
  1234.  
  1235. static Tk_RestrictAction
  1236. GrabRestrictProc(arg, eventPtr)
  1237.     ClientData arg;
  1238.     XEvent *eventPtr;
  1239. {
  1240.     GrabInfo *info = (GrabInfo *) arg;
  1241.     int mode, diff;
  1242.  
  1243.     /*
  1244.      * The diff caculation is trickier than it may seem.  Don't forget
  1245.      * that serial numbers can wrap around, so can't compare the two
  1246.      * serial numbers directly.
  1247.      */
  1248.  
  1249.     diff = eventPtr->xany.serial - info->serial;
  1250.     if ((eventPtr->type == EnterNotify)
  1251.         || (eventPtr->type == LeaveNotify)) {
  1252.     mode = eventPtr->xcrossing.mode;
  1253.     } else if ((eventPtr->type == FocusIn)
  1254.         || (eventPtr->type == FocusOut)) {
  1255.     mode = eventPtr->xfocus.mode;
  1256.     } else {
  1257.     mode = NotifyNormal;
  1258.     }
  1259.     if ((info->display != eventPtr->xany.display) || (mode == NotifyNormal)
  1260.         || (diff < 0)) {
  1261.     return TK_DEFER_EVENT;
  1262.     } else {
  1263.     return TK_DISCARD_EVENT;
  1264.     }
  1265. }
  1266.  
  1267. /*
  1268.  *----------------------------------------------------------------------
  1269.  *
  1270.  * QueueGrabWindowChange --
  1271.  *
  1272.  *    This procedure queues a special event in the Tcl event queue,
  1273.  *    which will cause the "grabWinPtr" field for the display to get
  1274.  *    modified when the event is processed.  This is needed to make
  1275.  *    sure that the grab window changes at the proper time relative
  1276.  *    to grab-related enter and leave events that are also in the
  1277.  *    queue.  In particular, this approach works even when multiple
  1278.  *    grabs and ungrabs happen back-to-back.
  1279.  *
  1280.  * Results:
  1281.  *    None.
  1282.  *
  1283.  * Side effects:
  1284.  *    DispPtr->grabWinPtr will be modified later (by GrabWinEventProc)
  1285.  *    when the event is removed from the grab event queue.
  1286.  *
  1287.  *----------------------------------------------------------------------
  1288.  */
  1289.  
  1290. static void
  1291. QueueGrabWindowChange(dispPtr, grabWinPtr)
  1292.     TkDisplay *dispPtr;        /* Display on which to change the grab
  1293.                  * window. */
  1294.     TkWindow *grabWinPtr;    /* Window that is to become the new grab
  1295.                  * window (may be NULL). */
  1296. {
  1297.     NewGrabWinEvent *grabEvPtr;
  1298.  
  1299.     grabEvPtr = (NewGrabWinEvent *) ckalloc(sizeof(NewGrabWinEvent));
  1300.     grabEvPtr->header.proc = GrabWinEventProc;
  1301.     grabEvPtr->dispPtr = dispPtr;
  1302.     if (grabWinPtr == NULL) {
  1303.     grabEvPtr->grabWindow = None;
  1304.     } else {
  1305.     grabEvPtr->grabWindow = grabWinPtr->window;
  1306.     }
  1307.     Tcl_QueueEvent(&grabEvPtr->header, TCL_QUEUE_MARK);
  1308.     dispPtr->eventualGrabWinPtr = grabWinPtr;
  1309. }
  1310.  
  1311. /*
  1312.  *----------------------------------------------------------------------
  1313.  *
  1314.  * GrabWinEventProc --
  1315.  *
  1316.  *    This procedure is invoked as a handler for Tcl_Events of type
  1317.  *    NewGrabWinEvent.  It updates the current grab window field in
  1318.  *    a display.
  1319.  *
  1320.  * Results:
  1321.  *    Returns 1 if the event was processed, 0 if it should be deferred
  1322.  *    for processing later.
  1323.  *
  1324.  * Side effects:
  1325.  *    The grabWinPtr field is modified in the display associated with
  1326.  *    the event.
  1327.  *
  1328.  *----------------------------------------------------------------------
  1329.  */
  1330.  
  1331. static int
  1332. GrabWinEventProc(evPtr, flags)
  1333.     Tcl_Event *evPtr;        /* Event of type NewGrabWinEvent. */
  1334.     int flags;            /* Flags argument to Tk_DoOneEvent: indicates
  1335.                  * what kinds of events are being processed
  1336.                  * right now. */
  1337. {
  1338.     NewGrabWinEvent *grabEvPtr = (NewGrabWinEvent *) evPtr;
  1339.  
  1340.     grabEvPtr->dispPtr->grabWinPtr = (TkWindow *) Tk_IdToWindow(
  1341.         grabEvPtr->dispPtr->display, grabEvPtr->grabWindow);
  1342.     return 1;
  1343. }
  1344.  
  1345. /*
  1346.  *----------------------------------------------------------------------
  1347.  *
  1348.  * FindCommonAncestor --
  1349.  *
  1350.  *    Given two windows, this procedure finds their least common
  1351.  *    ancestor and also computes how many levels up this ancestor
  1352.  *    is from each of the original windows.
  1353.  *
  1354.  * Results:
  1355.  *    If the windows are in different applications or top-level
  1356.  *    windows, then NULL is returned and *countPtr1 and *countPtr2
  1357.  *    are set to the depths of the two windows in their respective
  1358.  *    top-level windows (1 means the window is a top-level, 2 means
  1359.  *    its parent is a top-level, and so on).  Otherwise, the return
  1360.  *    value is a pointer to the common ancestor and the counts are
  1361.  *    set to the distance of winPtr1 and winPtr2 from this ancestor
  1362.  *    (1 means they're children, 2 means grand-children, etc.).
  1363.  *
  1364.  * Side effects:
  1365.  *    None.
  1366.  *
  1367.  *----------------------------------------------------------------------
  1368.  */
  1369.  
  1370. static TkWindow *
  1371. FindCommonAncestor(winPtr1, winPtr2, countPtr1, countPtr2)
  1372.     TkWindow *winPtr1;        /* First window.   May be NULL. */
  1373.     TkWindow *winPtr2;        /* Second window.  May be NULL. */
  1374.     int *countPtr1;        /* Store nesting level of winPtr1 within
  1375.                  * common ancestor here. */
  1376.     int *countPtr2;        /* Store nesting level of winPtr2 within
  1377.                  * common ancestor here. */
  1378. {
  1379.     register TkWindow *winPtr;
  1380.     TkWindow *ancestorPtr;
  1381.     int count1, count2, i;
  1382.  
  1383.     /*
  1384.      * Mark winPtr1 and all of its ancestors with a special flag bit.
  1385.      */
  1386.  
  1387.     if (winPtr1 != NULL) {
  1388.     for (winPtr = winPtr1; winPtr != NULL; winPtr = winPtr->parentPtr) {
  1389.         winPtr->flags |= TK_GRAB_FLAG;
  1390.         if (winPtr->flags & TK_TOP_LEVEL) {
  1391.         break;
  1392.         }
  1393.     }
  1394.     }
  1395.  
  1396.     /*
  1397.      * Search upwards from winPtr2 until an ancestor of winPtr1 is
  1398.      * found or a top-level window is reached.
  1399.      */
  1400.  
  1401.     winPtr = winPtr2;
  1402.     count2 = 0;
  1403.     ancestorPtr = NULL;
  1404.     if (winPtr2 != NULL) {
  1405.     for (; winPtr != NULL; count2++, winPtr = winPtr->parentPtr) {
  1406.         if (winPtr->flags & TK_GRAB_FLAG) {
  1407.         ancestorPtr = winPtr;
  1408.         break;
  1409.         }
  1410.         if (winPtr->flags & TK_TOP_LEVEL)  {
  1411.         count2++;
  1412.         break;
  1413.         }
  1414.     }
  1415.     }
  1416.  
  1417.     /*
  1418.      * Search upwards from winPtr1 again, clearing the flag bits and
  1419.      * remembering how many levels up we had to go.
  1420.      */
  1421.  
  1422.     if (winPtr1 == NULL) {
  1423.     count1 = 0;
  1424.     } else {
  1425.     count1 = -1;
  1426.     for (i = 0, winPtr = winPtr1; winPtr != NULL;
  1427.         i++, winPtr = winPtr->parentPtr) {
  1428.         winPtr->flags &= ~TK_GRAB_FLAG;
  1429.         if (winPtr == ancestorPtr) {
  1430.         count1 = i;
  1431.         }
  1432.         if (winPtr->flags & TK_TOP_LEVEL) {
  1433.         if (count1 == -1) {
  1434.             count1 = i+1;
  1435.         }
  1436.         break;
  1437.         }
  1438.     }
  1439.     }
  1440.  
  1441.     *countPtr1 = count1;
  1442.     *countPtr2 = count2;
  1443.     return ancestorPtr;
  1444. }
  1445.  
  1446. /*
  1447.  *----------------------------------------------------------------------
  1448.  *
  1449.  * TkPositionInTree --
  1450.  *
  1451.  *    Compute where the given window is relative to a particular
  1452.  *    subtree of the window hierarchy.
  1453.  *
  1454.  * Results:
  1455.  *
  1456.  *    Returns TK_GRAB_IN_TREE if the window is contained in the
  1457.  *    subtree.  Returns TK_GRAB_ANCESTOR if the window is an
  1458.  *    ancestor of the subtree, in the same toplevel.  Otherwise
  1459.  *    it returns TK_GRAB_EXCLUDED.
  1460.  *
  1461.  * Side effects:
  1462.  *    None.
  1463.  *
  1464.  *----------------------------------------------------------------------
  1465.  */
  1466.  
  1467. int
  1468. TkPositionInTree(winPtr, treePtr)
  1469.     TkWindow *winPtr;        /* Window to be checked. */
  1470.     TkWindow *treePtr;        /* Root of tree to compare against. */
  1471. {
  1472.     TkWindow *winPtr2;
  1473.  
  1474.     for (winPtr2 = winPtr; winPtr2 != treePtr;
  1475.        winPtr2 = winPtr2->parentPtr) {
  1476.     if (winPtr2 == NULL) {
  1477.         for (winPtr2 = treePtr; winPtr2 != NULL;
  1478.             winPtr2 = winPtr2->parentPtr) {
  1479.         if (winPtr2 == winPtr) {
  1480.             return TK_GRAB_ANCESTOR;
  1481.         }
  1482.         if (winPtr2->flags & TK_TOP_LEVEL) {
  1483.             break;
  1484.         }
  1485.         }
  1486.         return TK_GRAB_EXCLUDED;
  1487.     }
  1488.     }
  1489.     return TK_GRAB_IN_TREE;
  1490. }
  1491.  
  1492. /*
  1493.  *----------------------------------------------------------------------
  1494.  *
  1495.  * TkGrabState --
  1496.  *
  1497.  *    Given a window, this procedure returns a value that indicates
  1498.  *    the grab state of the application relative to the window.
  1499.  *
  1500.  * Results:
  1501.  *    The return value is one of three things:
  1502.  *        TK_GRAB_NONE -    no grab is in effect.
  1503.  *        TK_GRAB_IN_TREE -   there is a grab in effect, and winPtr
  1504.  *                is in the grabbed subtree.
  1505.  *        TK_GRAB_ANCESTOR -  there is a grab in effect;  winPtr is
  1506.  *                an ancestor of the grabbed window, in
  1507.  *                the same toplevel.
  1508.  *        TK_GRAB_EXCLUDED -    there is a grab in effect; winPtr is
  1509.  *                outside the tree of the grab and is not
  1510.  *                an ancestor of the grabbed window in the
  1511.  *                same toplevel.
  1512.  *
  1513.  * Side effects:
  1514.  *    None.
  1515.  *
  1516.  *----------------------------------------------------------------------
  1517.  */
  1518.  
  1519. int
  1520. TkGrabState(winPtr)
  1521.     TkWindow *winPtr;        /* Window for which grab information is
  1522.                  * needed. */
  1523. {
  1524.     TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr;
  1525.  
  1526.     if (grabWinPtr == NULL) {
  1527.     return TK_GRAB_NONE;
  1528.     }
  1529.     if ((winPtr->mainPtr != grabWinPtr->mainPtr)
  1530.         && !(winPtr->dispPtr->grabFlags & GRAB_GLOBAL)) {
  1531.     return TK_GRAB_NONE;
  1532.     }
  1533.  
  1534.     return TkPositionInTree(winPtr, grabWinPtr);
  1535. }
  1536.